package com.hondee.fuzzyquery.query.targetdefinition;

import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.hondee.fuzzyquery.query.IQueryTarget;

public class XMLResource implements ITargetResource {

	private String path;

	public XMLResource(String path) {
		this.path = path;
	}

	@Override
	public List<IQueryTarget> getTargets() {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

		DocumentBuilder builder;

		List<IQueryTarget> targetList = new ArrayList<>();

		try {
			builder = factory.newDocumentBuilder();
			Document document = builder.parse(new InputSource(getClass().getClassLoader().getResourceAsStream(path)));
			NodeList nodeList = document.getDocumentElement().getChildNodes();
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node item = nodeList.item(i);

				if (!item.hasAttributes()) {
					continue;
				}

				NamedNodeMap attributes = item.getAttributes();

				Class<?> clz = null;
				if (attributes != null) {
					Node namedItem = attributes.getNamedItem("class");
					if (namedItem == null) {
						throw new RuntimeException("class attribute is null.");
					}
					String className = namedItem.getNodeValue().trim();

					clz = Class.forName(className);

				}
				IQueryTarget newInstance = (IQueryTarget) clz.newInstance();
				if (item instanceof Element) {
					NodeList childNodes = item.getChildNodes();
					for (int j = 0; j < childNodes.getLength(); j++) {
						Node property = childNodes.item(j);
						if (property.getNodeName().equals("#text")) {
							continue;
						}
						NodeList childNodes2 = property.getChildNodes();
						Field declaredField = getDeclaredField(clz, property.getNodeName());
						if (childNodes2.getLength() > 1) {
							String[] extractList = XMLResource.extractList(childNodes2);
							setFieldValue(newInstance, declaredField, extractList);
						} else {
							if (property instanceof Element) {
								Node lastChild = property.getLastChild();
								String content = lastChild.getTextContent().trim();
								if (declaredField != null) {
									setFieldValue(newInstance, declaredField, content);
								}
							}
						}
					}
				}
				targetList.add(newInstance);
			}

		} catch (ParserConfigurationException | SAXException | IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		return targetList;
	}

	public static String[] extractList(NodeList itemList) {
		Node list = null;
		for (int j = 0; j < itemList.getLength(); j++) {
			Node item = itemList.item(j);
			if (item.getNodeName().equals("list")) {
				list = item;
			}
		}
		if (list == null) {
			return new String[0];
		}
		NodeList childNodes = list.getChildNodes();
		List<String> targetList = new ArrayList<>();

		for (int i = 0; i < childNodes.getLength(); i++) {
			Node item = childNodes.item(i);
			if (item instanceof Element && item.getNodeName().equals("item")) {
				targetList.add(item.getTextContent().trim());
			}
		}
		String[] targetArray = new String[targetList.size()];
		for(int i =0;i<targetList.size();i++){
			targetArray[i] = targetList.get(i);
		}
		return targetArray;
	}

	public static Field getDeclaredField(Class<?> clazz, String fieldName)
			throws NoSuchFieldException, SecurityException {
		Field field = null;

		for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
			try {
				field = clazz.getDeclaredField(fieldName);
			} catch (Exception e) {
			}
			if (field != null) {
				return field;
			}
		}

		return null;
	}

	public static void setFieldValue(Object newInstance, Field field, Object content) {
		try {
			field.setAccessible(true);
			Class<?> type = field.getType();
			if (type == Integer.class) {
				field.set(newInstance, Integer.parseInt(content.toString()));
			} else if (type == Boolean.class) {
				field.set(newInstance, Boolean.parseBoolean(content.toString()));
			} else if (type == String.class) {
				field.set(newInstance, content);
			} else if (type == Array.class) {
				field.set(newInstance, content);
			}else{
				field.set(newInstance, content);
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

	}

}
